package analyticsServer;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import managementClient.IClientRMI;

/**
 *
 * @author Group 95
 */
public class AnalyticsServer  extends UnicastRemoteObject implements IAnalyticsServer,Runnable
{
    public static final String NAME = "Analytics";
    private static final Logger logger = Logger.getLogger("AnalyticsServer");
    
    private final List<Event> arEventBuffer;
    private final SubscriptionCollection subscriptions;
    private volatile boolean bStop;
    private final EventCalculator calculator;

    
    
    public AnalyticsServer() throws RemoteException 
    {
        super();
        logger.setLevel(Level.OFF);
        this.arEventBuffer=new ArrayList<Event>(100);
        this.subscriptions=new SubscriptionCollection();
        this.calculator=new EventCalculator();
    }
    
    
    
    @Override
    public String subscribe(String regex, IClientRMI callback) throws RemoteException 
    {
        logger.info("AnalyticsServer new subscription: " + regex);
        return subscriptions.add(callback, regex);
    }

    /**
     * Events are just saved here, they are consumed by a sperate Thread to improve Performace
     * @param event
     * @throws RemoteException 
     */
    @Override
    public void processEvent(Event event) throws RemoteException 
    {
        synchronized(arEventBuffer)
        {
            arEventBuffer.add(event);
            arEventBuffer.notify();
        }
    }

    @Override
    public boolean unsubscribe(String id) throws RemoteException 
    {
        logger.info("AnalyticsServer unsubscribe: id= " + id);
        return subscriptions.removeSubscription(id);
    }

    @Override
    public void run() {
        while (!bStop) {
            synchronized (arEventBuffer) {
                for (Event recEvent : arEventBuffer) {
                    final List<StatisticsEvent> calculated = recEvent.calculate(calculator);
                    sendEvent(recEvent);
                    if (calculated != null) {
                        for (StatisticsEvent event : calculated) {
                            sendEvent(event);
                        }
                    }
                }
                arEventBuffer.clear();
                try {
                    arEventBuffer.wait();
                    logger.info("Exit AnalyticsServer run 1");
                } catch (InterruptedException ex) {
                    logger.info(("got interrupted, exciting"));
                }
            }
        }
        logger.info("Exit AnalyticsServer run");
    }

    /**
     * Checks if the event has to be sent to a client and sends it
     * @param event 
     */
    public void sendEvent(final Event event)
    {
        AnalyticsClient client=null;
        Iterator<AnalyticsClient> iterateClients=this.subscriptions.iterator();
        while(iterateClients.hasNext())
        {
            try
            {
                client=iterateClients.next();
                Iterator<Pattern> iteratePattern=client.iterator();
                while(iteratePattern.hasNext())
                {
                    Matcher matcher=iteratePattern.next().matcher(event.type);
                    if(matcher.matches())
                    {
                        client.getRMIClient().processEvent(event);
                        break;  //event should be sent just once (per client)
                    }
                }
            }catch(RemoteException ex)
            {
                logger.warning(("Got RemoteException from Client '"+client.getId()+"', connection lost?\nNo further Events will be sent to this client!"));
                subscriptions.remove(client.getId());
            }
        }
    }
        
    
    public void stop()
    {
        logger.info("Exit AnalyticsServer stop-method");
        this.bStop=true;
        synchronized(arEventBuffer)
        {
            arEventBuffer.notify();
        }
    }
}
